﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using Telerik.Web.Mvc.UI;
using Telerik.Web.Mvc.UI.Fluent;

namespace Fuse8.ContentManagementFramework.Web.MvcPowered.Components.Editors
{
    /// <summary>
    /// Contains extensions for using in MVC views
    /// </summary>
    public static class CmsEditorComponentHtmlHelperExtension
    {
        /// <summary>
        /// Creates builder for rendering editor
        /// </summary>
        /// <param name="helper"></param>
        /// <returns></returns>
        public static CmsEditorComponent CmsEditor(this HtmlHelper helper)
        {
            return new CmsEditorComponent(helper);
        }
    }

    /// <summary>
    /// Provides API for building editors
    /// </summary>
    public class CmsEditorComponent : IHtmlString
    {
        private string name;
        private CmsEditorMode editorMode = CmsEditorMode.Telerik;
        private Action<EditorBuilder> telerikCustomSetting;
        private string editorValue;
        private object attributes;
        private bool needEncodeValue;

        /// <summary>
        /// Current HtmlHelper
        /// </summary>
        public HtmlHelper Helper { get; protected set; }

        /// <summary>
        /// Inits component
        /// </summary>
        /// <param name="helper">Current HtmlHelper</param>
        public CmsEditorComponent(HtmlHelper helper)
        {
            Helper = helper;
        }

        /// <summary>
        /// Returns an HTML-encoded string.
        /// </summary>
        /// <returns>
        /// An HTML-encoded string.
        /// </returns>
        public string ToHtmlString()
        {
            return Render();
        }

        /// <summary>
        /// Renders component
        /// </summary>
        /// <returns></returns>
        public string Render()
        {
            switch (editorMode)
            {
                case CmsEditorMode.Telerik:
                    return BuildTelerikControl();        
                case CmsEditorMode.SimpleTextBox:
                    return Helper.TextArea(name, editorValue, attributes).ToHtmlString();                    
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// Sets mode for editor rendering
        /// </summary>
        /// <param name="value">Mode to set up</param>
        /// <returns></returns>
        public CmsEditorComponent Mode(CmsEditorMode value)
        {
            editorMode = value;
            return this;
        }

        /// <summary>
        /// Sets editor name
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public CmsEditorComponent Name(string value)
        {
            name = value;
            return this;
        }

        /// <summary>
        /// Sets editor value
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public CmsEditorComponent Value(string value)
        {
            editorValue = value;
            return this;
        }

        /// <summary>
        /// Sets custom telerik actions
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public CmsEditorComponent TelerikOptions(Action<EditorBuilder> value)
        {
            if (value != null)
            {
                telerikCustomSetting = value;
            }
            return this;
        }

        /// <summary>
        /// Sets editor Html attributes
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public CmsEditorComponent HtmlAttributes(object value)
        {
            if (value != null)
            {
                attributes = value;
            }

            return this;
        }

        /// <summary>
        /// INdicates whether it is needed to encode editor value
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public CmsEditorComponent Encode(bool value)
        {
            needEncodeValue = value;
            return this;
        }

        private string BuildTelerikControl()
        {
            var builder = Helper.Telerik().Editor().Name(name).Value(editorValue).Encode(needEncodeValue).HtmlAttributes(attributes);

            var settings = EditorSettingManager.Settings;
            if (settings != null)
            {
                //todo: think how avoid reflection
                BuildTools(settings, builder);
                BuildBrowser(settings, builder);
                BuildCssFiles(settings, builder);
            }

            if (telerikCustomSetting != null)
            {
                telerikCustomSetting(builder);
            }

            return builder.ToHtmlString();
        }

        private void BuildCssFiles(EditorSetting settings, EditorBuilder builder)
        {
            if (settings.CustomCssFiles != null)
            {
                foreach (var file in settings.CustomCssFiles)
                {
                    string fileToAdd = file;
                    builder.StyleSheets(a => a.Add(fileToAdd));
                }
            }
        }

        private void BuildBrowser(EditorSetting settings, EditorBuilder builder)
        {
            if (settings.BrowseSettings != null)
            {
                foreach (var setting in settings.BrowseSettings)
                {
                    var method = typeof(EditorFileBrowserSettingsBuilder).GetMethod(setting.Name, new Type[]{typeof(string), typeof(string)});
                    if (method != null)
                    {
                        BrowseSetting browseSetting = setting;
                        builder.FileBrowser(a => method.Invoke(a, new object[]{browseSetting.Action, browseSetting.Controller}));
                    }
                } 
            }
        }

        private void BuildTools(EditorSetting settings, EditorBuilder builder)
        {
            builder.Tools(a => a.Clear());
            foreach (var tool in settings.Tools)
            {
                switch (tool.Type)
                {
                    case EditorToolType.Standard:
                        AddStandardTool(builder, tool);
                        break;
                    case EditorToolType.Styles:
                        AddStyles(builder, tool);
                        break;
                    case EditorToolType.Snippets:
                        AddSnippets(builder, tool);
                        break;
                    case EditorToolType.Custom:
                        AddCustomTool(builder, tool);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        private void AddCustomTool(EditorBuilder builder, EditorTool tool)
        {
            IDictionary<string, object> dictionary = new Dictionary<string, object>();
            foreach (var attribute in tool.Attributes)
            {
                dictionary.Add(attribute.Name, attribute.Value.Replace("{id}", GetClientId()));
            }
            builder.Tools(a => a.Custom(c => c.HtmlAttributes(dictionary)));
        }

        private static void AddSnippets(EditorBuilder builder, EditorTool tool)
        {
            EditorTool snippetTool = tool;
            builder.Tools(a => a.Snippets(c =>
                                              {
                                                  foreach (var style in snippetTool.Snippets)
                                                  {
                                                      c.Add(style.Name, style.Text);
                                                  }
                                              }));
        }

        private static void AddStyles(EditorBuilder builder, EditorTool tool)
        {
            EditorTool styleTool = tool;
            builder.Tools(a => a.Styles(c =>
                                            {
                                                foreach (var style in styleTool.Styles)
                                                {
                                                    c.Add(style.Name, style.Value);
                                                }
                                            }));
        }

        private static void AddStandardTool(EditorBuilder builder, EditorTool tool)
        {
            var method = typeof (EditorToolFactory).GetMethod(tool.Name, new Type[0]);
            if (method != null)
            {
                builder.Tools(a => method.Invoke(a, null));
            }
        }

        protected string GetClientId()
        {
            string result = name;
            if (attributes != null)
            {
                var info = attributes.GetType().GetProperty("id");
                if (info != null)
                {
                    result = info.GetValue(attributes, null).ToString();
                }
            }

            return result;
        }
    }
}